///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_MAT_DETAIL_TRAITS_H_
#define _BUOLA_MAT_DETAIL_TRAITS_H_

#include <buola/mat/detail/forward.h>

namespace buola { namespace mat { namespace detail {

enum
{
    ACCESS_NO,
    ACCESS_ONCE,
    ACCESS_SLOW,
    ACCESS_FAST,
    ACCESS_DIRECT
};
    
template<typename tExpr>
struct MMatrixTraits;

template<typename tScalar>
struct MScalarTraits;

template<typename tExpr>
using OScalar=typename MMatrixTraits<ODecay<tExpr>>::TScalar;

template<typename tMat,int vAccess>
using QElementAccess=std::integral_constant<bool,(MMatrixTraits<ODecay<tMat>>::cElementAccess>=vAccess)>;

template<typename tExpr>
using QExprAssignable=QBool<MMatrixTraits<ODecay<tExpr>>::cExprAssignable>;

template<typename tExpr>
using QNonConstMatrix=QBool<MMatrixTraits<ODecay<tExpr>>::cIsMatrix&&!QIsConst<ORemoveRef<tExpr>>::value>;

template<typename tCVExpr>
using QAssignable=QOr<QExprAssignable<tCVExpr>,QNonConstMatrix<tCVExpr>>;

template<typename tMat>
using QEigenMappable=std::integral_constant<bool,MMatrixTraits<ODecay<tMat>>::cEigenMappable>;

template<typename tMat>
using QOneByOne=std::integral_constant<bool,MMatrixTraits<ODecay<tMat>>::cRows==1&&MMatrixTraits<ODecay<tMat>>::cCols==1>;

template<typename tLH,typename tRH>
using QOneByOneProduct=std::integral_constant<bool,MMatrixTraits<ODecay<tLH>>::cRows==1&&MMatrixTraits<ODecay<tRH>>::cCols==1>;

template<typename tMat>
using QVector=std::integral_constant<bool,MMatrixTraits<ODecay<tMat>>::cRows==1||MMatrixTraits<ODecay<tMat>>::cCols==1>;

template<typename tMat>
using QRowVector=QBool<MMatrixTraits<ODecay<tMat>>::cRows==1>;

template<typename tMat>
using QColVector=QBool<MMatrixTraits<ODecay<tMat>>::cCols==1>;

template<typename tMat,size_t vRows>
using QHasRows=QBool<MMatrixTraits<ODecay<tMat>>::cRows==vRows>;

template<typename tMat,size_t vCols>
using QHasCols=QBool<MMatrixTraits<ODecay<tMat>>::cCols==vCols>;

template<typename tMat,size_t vN>
using QHasRowsAndCols=QBool<MMatrixTraits<ODecay<tMat>>::cRows==vN&&MMatrixTraits<ODecay<tMat>>::cCols==vN>;

template<typename tExpr>
using QBoolean=std::is_same<OScalar<tExpr>,bool>;

template<typename tExpr>
using QBooleanVector=QAnd<QVector<tExpr>,QBoolean<tExpr>>;

template<typename tExpr>
using QMightAlias=QBool<MMatrixTraits<ODecay<tExpr>>::cMightAlias>;

template<typename tExpr>
struct MMatrixDerivedTraits
{
    typedef MMatrixTraits<tExpr> TTraits;

    typedef CMat<typename TTraits::TScalar,TTraits::cRows,TTraits::cCols> TMatrixType;
    typedef const tExpr& TConstNestType;
    typedef typename std::conditional<TTraits::cKeepConstness,tExpr&,const tExpr&>::type TNonConstNestType;
};

template<typename tOther>
using OMatrixType=typename MMatrixDerivedTraits<ODecay<tOther>>::TMatrixType;

template<typename tOther>
using OConstNestType=typename MMatrixDerivedTraits<ODecay<tOther>>::TConstNestType;

template<typename tOther>
using ONonConstNestType=typename MMatrixDerivedTraits<ODecay<tOther>>::TNonConstNestType;

template<typename tLH,typename tRH>
struct QSizesCompatible
{
private:
    typedef MMatrixTraits<ODecay<tLH>> TLH;
    typedef MMatrixTraits<ODecay<tRH>> TRH;

public:
    static const bool value=(TLH::cRows==cDynamic||TRH::cRows==cDynamic||TLH::cRows==TRH::cRows)&&
                            (TLH::cCols==cDynamic||TRH::cCols==cDynamic||TLH::cCols==TRH::cCols);
};

template<typename tExpr,int vAccess>
using OElementAccessible=OConditional<QElementAccess<tExpr,vAccess>,tExpr,OMatrixType<tExpr>>;

template<int vAccess,typename tExpr>
OElementAccessible<const tExpr&,vAccess> element_accessible(const tExpr &pExpr)
{
    return pExpr;
}

/*namespace detail*/ } /*namespace mat*/ } /*namespace buola*/ }

#endif
